#include "BufferedRandom3D.h"
BufferedRandom3D::BufferedRandom3D(uint xDimension, uint yDimension, uint zDimension, int chunkDimension, int seed)
{
	m_xSize = xDimension;
	m_ySize = yDimension;
	m_zSize = zDimension;
	m_chunkDimension = chunkDimension;
	m_uniqueWorldSeed = seed;
	m_blockLength = m_xSize * m_ySize * m_zSize;
}
BufferedRandom3D::~BufferedRandom3D(void)
{
}
bool BufferedRandom3D::GetBlockContainingChunk(int chunkX, int chunkZ, int *xOut, int *zOut)
{
	if((xOut == NULL) || (zOut == NULL))
		return false;
	
	int totalChunkX = (chunkX*m_chunkDimension);
	int totalChunkY = (chunkX*m_chunkDimension);

	int totalChunkRoundedX = totalChunkX - (totalChunkX % m_xSize);
	int totalChunkRoundedY = totalChunkY - (totalChunkY % m_ySize);

	(*xOut) = (int)floor((double)totalChunkRoundedX/(double)m_xSize);
	(*zOut) = (int)floor((double)totalChunkRoundedX/(double)m_xSize);

	return BlockExists(*xOut, *zOut);
}
bool BufferedRandom3D::BlockExists(int blockX, int blockZ)
{
	RandomMapBuffer::iterator it = m_randomBuffer.find(blockX);
	if(it != m_randomBuffer.end())
	{
		InnerRandomMapBuffer::iterator itInner = it->second.find(blockZ);
		if(itInner != it->second.end())
		{
			if(itInner->second != NULL)
			{
				return true;
			}
		}
	}
	return false;
}
float BufferedRandom3D::ValueAt(int x, int y, int z)
{
	//Get the block Containing x,z
	int modXBlockSize = (x % m_xSize);
	int modZBlockSize = (y % m_ySize);
	int blockX = x - modXBlockSize;
	int blockZ = z - modZBlockSize;
	float *pBlockPtr = GetBlock(blockX, blockZ);

	return pBlockPtr[modXBlockSize + (modZBlockSize * m_xSize * m_zSize) + (y * m_xSize)];
}
float* BufferedRandom3D::GetBlock(int blockX, int blockZ)
{
	//First check if the block already exists
	//First check the major order map (ordering is X->Z->data)
	RandomMapBuffer::iterator it = m_randomBuffer.find(blockX);
	InnerRandomMapBuffer::iterator itInner;
	if(it != m_randomBuffer.end())
	{
		itInner = it->second.find(blockZ);
		if(itInner != it->second.end())
		{
			if(itInner->second != NULL)
			{
				//Element found
				return itInner->second;
			}
		}
	}

	//Entry not found create it
	//Create a buffer to hold the values
	if(it == m_randomBuffer.end())
		m_randomBuffer[blockX] = InnerRandomMapBuffer();
	float *pBlockPtr = m_randomBuffer[blockX][blockZ] = new float[m_blockLength];

	//Seed the random generator with the x and y block index and the initial seed
	uint seedArray[] = {blockX, blockZ, m_uniqueWorldSeed};
	boost::random::seed_seq seeds(seedArray, seedArray + 3 * sizeof(int));
	BR3D_RnEngine rnEngine(seeds);
	BR3D_Distributor dist(0.0f,1.0f);
	BR3D_Generator rng(rnEngine, dist);

	//Generate all the numbers for this buffer
	int index = 0;
	while(index < m_blockLength)
	{
		pBlockPtr[index] = rng();
		++index;
	}

	//Output data
	//Data is ordered X->Y->Z, to index: X + (Y*XSize) + (Z*XSize*YSize)
	return pBlockPtr;
}